package com.budwk.app.util.poiUtils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.ParagraphFormat;
import com.aspose.words.SaveFormat;
import com.budwk.app.common.config.AppException;
import com.budwk.app.exception.Asserts;
import com.budwk.app.exception.ServiceException;
import com.budwk.app.oss.models.SysOss;
import com.budwk.app.oss.services.SysOssService;
import com.budwk.app.sqfw.component.CommonComponent;
import com.budwk.app.sqfw.vo.GenerateWordAndPdfUrlVO;
import com.budwk.app.sqfwV2.component.DocumentDownComponent;
import com.budwk.app.util.poiUtils.excelPictureConfigBO.DynamicExcelFileCreateParamBO;
import com.budwk.app.util.poiUtils.excelPictureConfigBO.ExcelFileCreateParamBO;
import com.budwk.app.util.poiUtils.nutzBeanUtils.NutzIocUtils;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.spire.doc.FileFormat;
import lombok.extern.slf4j.Slf4j;
import nl.basjes.shaded.org.springframework.core.io.ClassPathResource;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlOptions;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.impl.PropertiesProxy;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.springframework.util.ObjectUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ZY.Wang
 * @since 2023/7/24
 */
@Slf4j
public class CommonFormOfficeUtils {

    /**
     * word 输出流转 pdf输出流
     *
     * @param wordOutputStream word 输出流
     * @return pdf输出流
     */
    public static ByteArrayOutputStream wordToPdfV2(ByteArrayOutputStream wordOutputStream) {
        ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
        InputStream asposeLicInputStream = null;
        InputStream wordInputStream = null;
        try {
            asposeLicInputStream = new ClassPathResource("/license.xml").getInputStream();
            License asposeLic = new License();
            asposeLic.setLicense(asposeLicInputStream);

            wordInputStream = new ByteArrayInputStream(wordOutputStream.toByteArray());
            Document wordDocument = new Document(wordInputStream);

            ParagraphFormat pf = wordDocument.getStyles().getDefaultParagraphFormat();
            pf.clearFormatting();
            wordDocument.save(pdfOutputStream, SaveFormat.PDF);

            return pdfOutputStream;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(asposeLicInputStream);
            IoUtil.close(wordInputStream);
        }

        return null;
    }

    /**
     * 根据word模板 参数 服务类型 开始时间 创建word pdf 并且上传
     *
     * @param paramMap     参数
     * @param templateName 模板名称
     * @param fileName     文件名
     * @param formType     服务类型
     * @param startTime    开始时间
     * @return 结果vo
     */
    public static GenerateWordAndPdfUrlVO generateWordPdfAndUpload(Map<String, Object> paramMap, String templateName, String fileName, String formType, long startTime) {

        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        ByteArrayOutputStream pdfOutputStream = null;
        try {

            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();

            String filePathStr = getTemplatePathByOS(templateName);
            XWPFTemplate template = XWPFTemplate.compile(filePathStr, builder.build()).render(paramMap);

            String wordUrl = "", pdfUrl = "";

            template.write(wordOutPutStream);

            // 上传word
            String wordFileName = "tempFireHidden.docx";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".docx";
            }
            SysOss sysOss = uploadFile(wordOutPutStream, wordFileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", formType, sysOss));
            wordUrl = getFilePathFromUploadFileUrl(sysOss);

            // 转为pdf
            pdfOutputStream = wordToPdfV2(wordOutPutStream);

            // 上传pdf
            if (pdfOutputStream == null) throw new ServiceException("pdf转换出错");
            String pdfFileName = "temp.pdf";
            if (StringUtils.isNotBlank(fileName)) {
                pdfFileName = fileName + ".pdf";
            }

            System.out.println("开始上传pdf");
            SysOss pdfSysOss = uploadFile(pdfOutputStream, pdfFileName);
            System.out.println(StrFormatter.format("{}pdf文件上传至华为云，返回结果:{}", formType, pdfSysOss));
            pdfUrl = getFilePathFromUploadFileUrl(pdfSysOss);

            GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
            resultVO.setWordUrl(wordUrl);
            resultVO.setPdfUrl(pdfUrl);
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);

            return resultVO;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            IoUtil.close(pdfOutputStream);
            IoUtil.close(wordOutPutStream);
        }
    }

    /**
     * 根据模板创建word 输出流
     *
     * @param paramMap     参数
     * @param templatePath 模板路径
     * @param formType     服务类型
     * @param startTime    开始时间
     * @return 输出流
     */
    public static ByteArrayOutputStream generateWordDocumentWithPath(Map<String, Object> paramMap, String templatePath, String formType, long startTime) {
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        try {
            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();

            String filePathStr = getTemplatePathByName(templatePath);
            XWPFTemplate template = XWPFTemplate.compile(filePathStr, builder.build()).render(paramMap);
            template.write(wordOutPutStream);

            System.out.println(StrFormatter.format("{}word生成成功，耗时{}", formType, (System.currentTimeMillis() - startTime)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wordOutPutStream;
    }

    /**
     * 创建一条空白的照片
     *
     * @return 字节数组
     */
    public static byte[] createBlackImage() {
        try {
            // 定义图片的宽度和高度
            //int width = 80;
            //int height = 50;
            int width = 1;
            int height = 1;

            // 创建一个空白图片
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            // 填充图片的背景色（可选）
            // 如果不填充背景色，默认是黑色
            int backgroundColor = 0xFFFFFFFF; // 白色，格式为0xRRGGBB（红绿蓝分量）
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    image.setRGB(x, y, backgroundColor);
                }
            }

            // 将图片写入OutputStream
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", outputStream);

            // 从outputStream中获取字节数组
            byte[] imageBytes = outputStream.toByteArray();

            outputStream.close();

            return imageBytes;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static List<Map<String, PictureRenderData>> addWordPicture(List<String> imageUrlList, String itemName, Float targetLength) {
        return addWordPicture(imageUrlList, itemName, targetLength, targetLength);
    }

    public static List<Map<String, PictureRenderData>> addWordPicture(List<String> imageUrlList, String itemName, Float targetWidth, Float targetHeight) {

        List<Map<String, PictureRenderData>> list = Lists.newArrayList();
        if (ObjectUtils.isEmpty(imageUrlList)) {
            return list;
        }
        if (targetWidth == null) targetWidth = 14.0F;
        for (String imageUrl : imageUrlList) {
            Map<String, PictureRenderData> map = Maps.newHashMap();

            if (StringUtils.isBlank(imageUrl)) {
                byte[] blackImage = createBlackImage();
                map.put(itemName, Pictures.ofBytes(blackImage).create());
            } else {
//                if (imageUrl.startsWith("/"))
//                    imageUrl = NutzIocUtils.getOssPrefix() + imageUrl;
                // 从网络上读取图片然后得到输入流和宽高
//                UrlImageInfo urlImageInfo = new UrlImageInfo(imageUrl, targetWidth, targetHeight);
                UrlImageInfo urlImageInfo = CommonComponent.getImageUrl(imageUrl, targetWidth, targetHeight);
                PictureRenderData pictureRenderData = Pictures.ofBytes(urlImageInfo.getBytes()).sizeInCm(urlImageInfo.getTargetWidth(), urlImageInfo.getTargetHeight()).create();
                map.put(itemName, pictureRenderData);
            }
            list.add(map);
        }
        return list;
    }

    public static List<Map<String, PictureRenderData>> addWordPicture(String imageUrl, String itemName, Float targetLength) {
        List<String> imageList = Lists.newArrayList(imageUrl.split(","));
        return addWordPicture(imageList, itemName, targetLength);
    }

    public static PictureRenderData addWordPicture(String imageUrl, Float targetLength) {

        if (targetLength == null) targetLength = 14.0F;

        if (StringUtils.isBlank(imageUrl)) {
            byte[] blackImage = createBlackImage();
            return Pictures.ofBytes(blackImage).create();
        } else {
            // wzy 2023-8-16 因为有些时候这个字段是多张照片，所以这个时候就默认取第一张
            List<String> list = Lists.newArrayList(imageUrl.split(","));
            imageUrl = list.get(0);
            UrlImageInfo urlImageInfo = CommonComponent.getImageUrl(imageUrl, targetLength, null);
            return Pictures.ofBytes(urlImageInfo.getBytes()).sizeInCm(urlImageInfo.getTargetWidth(), urlImageInfo.getTargetHeight()).create();
        }
    }

    /**
     * 合并pdf并且上传
     *
     * @param pdfList pdf链接数组
     * @return 合并的pdf字符串
     */
    public static String createConvergePdfAndUpload(List<String> pdfList, String fileName) {
        if (pdfList.size() == 0) return null;
        if (pdfList.size() == 1) {
            String pdfUrl = pdfList.get(0);
            if (pdfUrl.startsWith("/"))
                pdfUrl =  pdfList.get(0);
            return pdfUrl;
        }

        String ossPrefix = NutzIocUtils.getOssPrefix();

        PDFMergerUtility pdfMerger = new PDFMergerUtility();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        List<InputStream> inputStreamList = Lists.newArrayList();
        SysOss wordUploadResponseBody = new SysOss();
        try {
            for (String pdfUrl : pdfList) {

                if (StringUtils.isBlank(pdfUrl)) continue;

                if (pdfUrl.startsWith("/"))
                    pdfUrl = ossPrefix + pdfUrl;
                try {
                    byte[] bytes = HttpUtil.downloadBytes(pdfUrl);
                    InputStream pdfInputStream = new ByteArrayInputStream(bytes);
                    pdfMerger.addSource(pdfInputStream);
                    inputStreamList.add(pdfInputStream);
                } catch (Exception e) {
                    log.error("createConvergePdfAndUpload downloadBytes pdfUrl: {}, error: ", pdfUrl, e);
                    System.out.println(StrFormatter.format("{} 链接pdf下载失败", pdfUrl));
                }
            }

            pdfMerger.setDestinationStream(outputStream);

            pdfMerger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());

            String wordFileName = "temp.pdf";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".pdf";
            }


            wordUploadResponseBody = uploadFile(outputStream, wordFileName);
            System.out.println(StrFormatter.format("合并的pdf文件上传至华为云，返回结果:{}", wordUploadResponseBody));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(outputStream);
            for (InputStream inputStream : inputStreamList) {
                IoUtil.close(inputStream);
            }
        }
        String huaweiOssPrefix = NutzIocUtils.getOssPrefixHuaWei();
        String proxy = NutzIocUtils.getOssProxy();
        return wordUploadResponseBody.getUrl().replace(huaweiOssPrefix,"").replace(proxy,"");
    }

    public static String createConvergePdf(List<String> pdfList, String fileName) {
        if (pdfList.size() == 0) return null;

        String ossPrefix = NutzIocUtils.getOssPrefix();

        PDFMergerUtility pdfMerger = new PDFMergerUtility();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        List<InputStream> inputStreamList = Lists.newArrayList();
        SysOss wordUploadResponseBody = new SysOss();
        try {
            for (String pdfUrl : pdfList) {

                if (StringUtils.isBlank(pdfUrl)) continue;

                if (pdfUrl.startsWith("/"))
                    pdfUrl = ossPrefix + pdfUrl;
                try {
                    byte[] bytes = HttpUtil.downloadBytes(pdfUrl);
                    InputStream pdfInputStream = new ByteArrayInputStream(bytes);
                    pdfMerger.addSource(pdfInputStream);
                    inputStreamList.add(pdfInputStream);
                } catch (Exception e) {
                    log.error("createConvergePdfAndUpload downloadBytes pdfUrl: {}, error: ", pdfUrl, e);
                    System.out.println(StrFormatter.format("{} 链接pdf下载失败", pdfUrl));
                }
            }

            pdfMerger.setDestinationStream(outputStream);

            pdfMerger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());

            String wordFileName = "temp.pdf";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".pdf";
            }

            wordUploadResponseBody = uploadFile(outputStream, wordFileName);
            System.out.println(StrFormatter.format("合并的pdf文件上传至华为云，返回结果:{}", wordUploadResponseBody));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(outputStream);
            for (InputStream inputStream : inputStreamList) {
                IoUtil.close(inputStream);
            }
        }

        return getFilePathFromUploadFileUrl(wordUploadResponseBody);
    }

    /**
     * word转pdf并且上传
     *
     * @param outputStreamList word的输出流数组
     * @param fileName         最后生成的文件名称
     * @param formType         类型，用于作为打印信息，无关紧要
     * @return 结果
     */
    public static CommonPdfAndWordResult word2PdfAndUpload(List<ByteArrayOutputStream> outputStreamList, String fileName, String formType) {
        long startTime = System.currentTimeMillis();

        ByteArrayOutputStream resultOS = new ByteArrayOutputStream();
        List<InputStream> pdfISList = Lists.newArrayList();
        List<ByteArrayOutputStream> pdfOSList = Lists.newArrayList();
        CommonPdfAndWordResult resultVO = new CommonPdfAndWordResult();

        InputStream asposeLicInputStream = null;

        try {
            asposeLicInputStream = new ClassPathResource("/license.xml").getInputStream();
            License asposeLic = new License();
            asposeLic.setLicense(asposeLicInputStream);

            for (ByteArrayOutputStream outputStream : outputStreamList) {
                if (outputStream == null) continue;
                if (outputStream.size() == 0) continue;
                ByteArrayInputStream wordInputStream = new ByteArrayInputStream(outputStream.toByteArray());

                Document pdfDocument = new Document(wordInputStream);

                ByteArrayOutputStream pdfOS = new ByteArrayOutputStream();
                pdfDocument.save(pdfOS, SaveFormat.PDF);

                pdfOSList.add(pdfOS);
            }

            PDFMergerUtility pdfMerger = new PDFMergerUtility();
            for (ByteArrayOutputStream pdfOS : pdfOSList) {
                byte[] bytes = pdfOS.toByteArray();
                InputStream pdfInputStream = new ByteArrayInputStream(bytes);
                pdfMerger.addSource(pdfInputStream);
                pdfISList.add(pdfInputStream);
            }

            pdfMerger.setDestinationStream(resultOS);

            pdfMerger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());

            String pdfUrl = "", wordUrl = "";


            // 上传pdf
            if (resultOS.size() == 0) throw new ServiceException("pdf转换出错");

            System.out.println("开始上传pdf");
            if (StringUtils.isBlank(fileName)) {
                fileName = "tempPdf.pdf";
            } else {
                fileName += ".pdf";
            }

            SysOss pdfUploadResponseBody = uploadFile(resultOS, fileName);
            System.out.println(StrFormatter.format("{}pdf文件上传至华为云，返回结果:{}", formType, pdfUploadResponseBody));
            pdfUrl = getFilePathFromUploadFileUrl(pdfUploadResponseBody);
            resultVO.setWordUrl(wordUrl);
            resultVO.setUrl(pdfUploadResponseBody.getUrl());
            resultVO.setPdfUrl(pdfUrl);
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            for (ByteArrayOutputStream outputStream : outputStreamList) {
                IoUtil.close(outputStream);
            }
            for (InputStream pdfIS : pdfISList) {
                IoUtil.close(pdfIS);
            }
            for (ByteArrayOutputStream pdfOS : pdfOSList) {
                IoUtil.close(pdfOS);
            }
            IoUtil.close(asposeLicInputStream);
            IoUtil.close(resultOS);
        }
        return resultVO;
    }

    /**
     * word转pdf并且上传
     * (这个方法返回的结果被加上了一些业务参数，违背了工具类单一原则，本来我想直接改掉这个方法的，但是使用的地方确实非常多，所以我做了一些修改作为妥协)
     *
     * @param outputStreamList word的输出流数组
     * @param fileName         最后生成的文件名称
     * @param formType         安全
     * @return 结果
     */
    public static GenerateWordAndPdfUrlVO wordToPdfAndUpload(List<ByteArrayOutputStream> outputStreamList, String fileName, String formType) {
        CommonPdfAndWordResult pdfResult = word2PdfAndUpload(outputStreamList, fileName, formType);
        GenerateWordAndPdfUrlVO wordVO = new GenerateWordAndPdfUrlVO();

        BeanUtil.copyProperties(pdfResult, wordVO);
        wordVO.setCreateConditionFlag(StringUtils.isNotBlank(pdfResult.getPdfUrl()) || StringUtils.isNotBlank(pdfResult.getWordUrl()));
        return wordVO;
    }

    /*******************************************************************************
     // 用于图片字符串url转为字节数组，方便插入excel
     ******************************************************************************/
    public static List<byte[]> addPictureToByteList(List<String> urlList) {
        if (urlList == null || urlList.size() == 0) {
            return Lists.newArrayList();
        }
        String huaweiOssPrefix = NutzIocUtils.getOssPrefix();
        List<byte[]> byteList = Lists.newArrayList();
        for (String urlStr : urlList) {
            final String[] finalUrlStr = {urlStr};
            try {
                long startTime = System.currentTimeMillis();
                log.info("开始下载图片1： {}", startTime);
                if (StringUtils.isNotBlank(finalUrlStr[0])) {
                    byte[] bytes = null;
                    String localFilePath = CommonFormOfficeForAdminUtils.getTempParentPictureFile(urlStr);
                    Path path = Paths.get(localFilePath);
                    if (Files.exists(path)) {
                        bytes = Files.readAllBytes(path);
                    } else {
                        log.info("需要远程下载的图片1： {}", finalUrlStr[0]);
                        if (StringUtils.isNotEmpty(finalUrlStr[0]) && finalUrlStr[0].startsWith("/")) {
                            finalUrlStr[0] = huaweiOssPrefix + finalUrlStr[0];
                        }
                        bytes = HttpUtil.downloadBytes(finalUrlStr[0]);
                    }
                    log.info("结束下载图片1： {}ms", (System.currentTimeMillis() - startTime));

                    // ywm 图片压缩
                    //byteList.add(PictureUtils.compressPicForScale(bytes, 100));

                    // ywm 图片不压缩
                    byteList.add(bytes);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return byteList;
    }

    public static List<byte[]> addPictureToByteList(String str, int picNum) {
        if (StringUtils.isBlank(str)) {
            return Lists.newArrayList();
        }
        List<String> urlList = Lists.newArrayList(str.split(","));
        if (picNum > urlList.size()) {
            picNum = urlList.size();
        }
        urlList = urlList.subList(0, picNum);
        if (urlList.size() == 0) {
            return Lists.newArrayList();
        }
        String huaweiOssPrefix = NutzIocUtils.getOssPrefix();
        String huaweiOssPrefix1 = NutzIocUtils.getOssPrefixHuaWei();
        List<byte[]> byteList = Lists.newArrayList();
        for (String urlStr : urlList) {
            final String[] finalUrlStr = {urlStr};
            try {
                long startTime = System.currentTimeMillis();
                log.info("开始下载图片2： {}", startTime);
                if (StringUtils.isNotBlank(finalUrlStr[0])) {
                    byte[] bytes = null;
                    String localFilePath = CommonFormOfficeForAdminUtils.getTempParentPictureFile(urlStr);
                    Path path = Paths.get(localFilePath);
                    File file = new File(localFilePath);
                    if (Files.exists(path)) {
                        bytes = Files.readAllBytes(path);
                    } else {
                        String fileUrl = finalUrlStr[0];
                        log.info("需要远程下载的图片2： {}", finalUrlStr[0]);
                        if (StringUtils.isNotEmpty(finalUrlStr[0]) && finalUrlStr[0].startsWith("/")) {
                            finalUrlStr[0] = huaweiOssPrefix + finalUrlStr[0];
                        }
                        try {
                            bytes = HttpUtil.downloadBytes(finalUrlStr[0]);
                        } catch (Exception e) {
                            if (StringUtils.isNotEmpty(fileUrl) && fileUrl.startsWith("/")) {
                                fileUrl = huaweiOssPrefix1 + fileUrl;
                            }
                            bytes = HttpUtil.downloadBytes(fileUrl);
                        }
                    }
                    log.info("结束下载图片2： {}ms", (System.currentTimeMillis() - startTime));
                    byteList.add(bytes);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return byteList;
    }

    /*******************************************************************************
     * 用于普通excel上传
     ******************************************************************************/
    public static String createExcelAndUpload(ExcelFileCreateParamBO excelFileCreateParam) {
        String templatePath = excelFileCreateParam.getTemplatePath();
        Class<?> clazz = excelFileCreateParam.getClazz();
        Map<String, Object> sheetParam = excelFileCreateParam.getSheetParam();
        List<Map<String, Object>> dataList = excelFileCreateParam.getDataList();
        Integer rowHeight = excelFileCreateParam.getRowHeight();
        Integer imgWidth = excelFileCreateParam.getImgWidth();
        String fileName = excelFileCreateParam.getFileName();
        Boolean listNotLastRow = excelFileCreateParam.getListNotLastRow();
        Boolean returnWebUrl = excelFileCreateParam.getReturnWebUrlFlag();

        if (listNotLastRow == null) listNotLastRow = false;
        if (returnWebUrl == null) returnWebUrl = true;

        FileInputStream templateIs = null;

        FileOutputStream outputStream = null;


        File file = null;
        try {
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            //边框
            contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
            contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
            contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
            contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
            HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                    new HorizontalCellStyleStrategy(contentWriteCellStyle, contentWriteCellStyle);
            file = getTempExcelFile(excelFileCreateParam.getFileName() + "-" + IdUtil.fastSimpleUUID() + ExcelTypeEnum.XLSX.getValue());
            outputStream = new FileOutputStream(file);
            templateIs = new FileInputStream(templatePath);

            // 添加这一行的目的是为了解决数据list之后还有单独的数据的情况,后续使用添加行的方式来解决
            // 但是这种方式非常 非常 非常的占内存，大数据谨慎使用
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

            ExcelWriterBuilder builder = null;
            if (clazz == null) builder = EasyExcel.write(outputStream);
            else builder = EasyExcel.write(outputStream, clazz);
            ExcelWriter excelWriter = builder
                    .autoCloseStream(false)
                    .registerConverter(new ExcelPicUrlConverter())
                    .registerWriteHandler(new CustomImageModifyHandler(rowHeight, imgWidth))
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    .withTemplate(templateIs)

                    .excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            if (listNotLastRow) {
                excelWriter.fill(sheetParam, fillConfig, writeSheet);
                excelWriter.fill(new FillWrapper("data", dataList), fillConfig, writeSheet);
            } else {
                excelWriter.fill(sheetParam, writeSheet);
                excelWriter.fill(new FillWrapper("data", dataList), writeSheet);
            }
            excelWriter.finish();

            outputStream.close();

            // 压缩再解压缩 一个是为了让excel文件变得更小，另外一个目的是为了让图片能够在wps中进行显示
            easyExcelRepairPic(file.getAbsolutePath());

            // wzy 2023-11-9 因为后台2.0是需要返回的文件便于压缩。所以如果是需要返回的是webUrl就上传
            if (returnWebUrl) {
                // 上传至华为云
                if (StringUtils.isBlank(fileName)) {
                    fileName = "tempFireHidden.xlsx";
                } else {
                    fileName += ".xlsx";
                }

                SysOss sysOss = uploadFile(file, fileName);
                System.out.println(sysOss.getUrl());
                String huaweiOssPrefix = NutzIocUtils.getOssPrefixHuaWei();
                String proxy = NutzIocUtils.getOssProxy();
                String url = getFilePathFromUploadFileUrl(sysOss);
                /*if (url.startsWith("/")) {
                    return String.format("%s%s", NutzIocUtils.getOssPrefix(), url);
                }*/
                return DocumentDownComponent.handleUrl(url);
//                return sysOss.getUrl().replace(huaweiOssPrefix,"").replace(proxy,"");
            } else {
                return file.getAbsolutePath();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            if (returnWebUrl) FileUtil.del(file);
            IoUtil.close(outputStream);
            IoUtil.close(templateIs);
        }
    }


    public static String dynamicExcelAndUpload(DynamicExcelFileCreateParamBO excelFileCreateParam) {
        String templatePath = excelFileCreateParam.getTemplatePath();
        Class<?> clazz = excelFileCreateParam.getClazz();
        Map<String, Object> sheetParam = excelFileCreateParam.getSheetParam();
        List<Map<String, Object>> dataList = excelFileCreateParam.getDataList();
        Integer rowHeight = excelFileCreateParam.getRowHeight();
        Integer imgWidth = excelFileCreateParam.getImgWidth();
        String fileName = excelFileCreateParam.getFileName();
        Boolean listNotLastRow = excelFileCreateParam.getListNotLastRow();
        Boolean returnWebUrl = excelFileCreateParam.getReturnWebUrlFlag();
        if (listNotLastRow == null) listNotLastRow = false;
        if (returnWebUrl == null) returnWebUrl = true;
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontHeightInPoints((short) 12);
        contentWriteFont.setFontName("宋体");
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        //边框
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);

        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(contentWriteCellStyle, contentWriteCellStyle);
        ByteArrayOutputStream screenTemplateOut = new ByteArrayOutputStream();
        FileInputStream templateIs = null;

        FileOutputStream outputStream = null;

        FileInputStream fileInputStream = null;

        InputStream screenTemporaryTemplate = null;

        File file = null;
        try {
            file = getTempExcelFile(excelFileCreateParam.getFileName() + "-" + IdUtil.fastSimpleUUID() + ExcelTypeEnum.XLSX.getValue());
            outputStream = new FileOutputStream(file);

            templateIs = new FileInputStream(templatePath);
            FillConfig fillConfigSecond = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
            ExcelWriterBuilder builder = null;
            if (clazz == null) builder = EasyExcel.write(screenTemplateOut);
            else builder = EasyExcel.write(screenTemplateOut, clazz);
            List<CellRangeAddress> list = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(excelFileCreateParam.getHeadDynamic())) {
                list.add(new CellRangeAddress(0, 0, 0, 7 + excelFileCreateParam.getHeadDynamic().size()));
            } else {
                list.add(new CellRangeAddress(0, 0, 0, 7));
            }

            ExcelWriter excelWriterFirst = builder
                    .autoCloseStream(false)
                    .registerWriteHandler(new CustomImageModifyHandler(rowHeight, imgWidth))
                    .registerWriteHandler(new ExcelFillCellMergeHandler(0, list))
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    .withTemplate(templateIs)
                    .excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            excelWriterFirst.fill(sheetParam, writeSheet);
            if (CollectionUtil.isNotEmpty(excelFileCreateParam.getHeadDynamic())) {
                excelWriterFirst.fill(new FillWrapper("headDynamic", excelFileCreateParam.getHeadDynamic()), fillConfigSecond, writeSheet);
            }
            excelWriterFirst.fill(new FillWrapper("data", dataList), writeSheet);


            excelWriterFirst.finish();

            // 需要导出的临时模板文件
            byte[] bytes = screenTemplateOut.toByteArray();

            screenTemporaryTemplate = new ByteArrayInputStream(bytes);

            // 配置横向填充
            ExcelWriter excelWriterSecond = EasyExcel
                    .write(outputStream)
                    .autoCloseStream(false)
                    .registerWriteHandler(new CustomImageModifyHandler(rowHeight, imgWidth))
                    .registerWriteHandler(horizontalCellStyleStrategy)
                    .withTemplate(screenTemporaryTemplate)
                    .excelType(ExcelTypeEnum.XLSX).build();
            if (CollectionUtil.isNotEmpty(excelFileCreateParam.getDataDynamic())) {
                excelWriterSecond.fill(new FillWrapper("dataDynamic", excelFileCreateParam.getDataDynamic()), fillConfigSecond, writeSheet);
            }
            excelWriterSecond.finish();

            // wzy 2023-11-9 因为后台2.0是需要返回的文件便于压缩。所以如果是需要返回的是webUrl就上传
            if (returnWebUrl) {
                // 上传至华为云
                if (StringUtils.isBlank(fileName)) {
                    fileName = "tempFireHidden.xlsx";
                } else {
                    fileName += ".xlsx";
                }

                SysOss sysOss = uploadFile(file, fileName);
                System.out.println(sysOss.getUrl());
                String huaweiOssPrefix = NutzIocUtils.getOssPrefixHuaWei();
                String proxy = NutzIocUtils.getOssProxy();
                return sysOss.getUrl().replace(huaweiOssPrefix,"").replace(proxy,"");
            } else {
                return file.getAbsolutePath();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            // if (returnWebUrl) FileUtil.del(file);
            IoUtil.close(outputStream);
            IoUtil.close(templateIs);
            IoUtil.close(fileInputStream);
            IoUtil.close(screenTemporaryTemplate);
        }
    }

    /*******************************************************************************
     * 用于普通excel上传
     ******************************************************************************/
    public static String createWQMExcelAndUpload(ExcelFileCreateParamBO excelFileCreateParam) {
        String templatePath = excelFileCreateParam.getTemplatePath();
        Class<?> clazz = excelFileCreateParam.getClazz();
        Map<String, Object> sheetParam = excelFileCreateParam.getSheetParam();
        List<Map<String, Object>> dataList = excelFileCreateParam.getDataList();
        Integer rowHeight = excelFileCreateParam.getRowHeight();
        String fileName = excelFileCreateParam.getFileName();
        Boolean listNotLastRow = excelFileCreateParam.getListNotLastRow();
        Boolean returnWebUrl = excelFileCreateParam.getReturnWebUrlFlag();

        if (listNotLastRow == null) listNotLastRow = false;
        if (returnWebUrl == null) returnWebUrl = true;

        FileInputStream templateIs = null;

        FileOutputStream outputStream = null;

        File file = null;
        try {
            file = getTempExcelFile(excelFileCreateParam.getFileName() + "-" + IdUtil.fastSimpleUUID() + ExcelTypeEnum.XLSX.getValue());
            outputStream = new FileOutputStream(file);
            templateIs = new FileInputStream(templatePath);

            // 添加这一行的目的是为了解决数据list之后还有单独的数据的情况,后续使用添加行的方式来解决
            // 但是这种方式非常 非常 非常的占内存，大数据谨慎使用
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

            ExcelWriterBuilder builder = null;
            if (clazz == null) builder = EasyExcel.write(outputStream);
            else builder = EasyExcel.write(outputStream, clazz);
            ExcelWriter excelWriter = builder
                    .autoCloseStream(false)
                    .registerConverter(new ExcelPicUrlConverter())
                    .registerWriteHandler(new WeiQingMenImageModifyHandler(rowHeight))
                    .withTemplate(templateIs)
                    .excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            if (listNotLastRow) {
                excelWriter.fill(sheetParam, fillConfig, writeSheet);
                excelWriter.fill(new FillWrapper("data", dataList), fillConfig, writeSheet);
            } else {
                excelWriter.fill(sheetParam, writeSheet);
                excelWriter.fill(new FillWrapper("data", dataList), writeSheet);
            }
            excelWriter.finish();

            outputStream.close();

            // 压缩再解压缩 一个是为了让excel文件变得更小，另外一个目的是为了让图片能够在wps中进行显示
            easyExcelRepairPic(file.getAbsolutePath());

            // wzy 2023-11-9 因为后台2.0是需要返回的文件便于压缩。所以如果是需要返回的是webUrl就上传
            if (returnWebUrl) {
                // 上传至华为云
                if (StringUtils.isBlank(fileName)) {
                    fileName = "tempFireHidden.xlsx";
                } else {
                    fileName += ".xlsx";
                }

                SysOss sysOss = uploadFile(file, fileName);
                System.out.println(sysOss.getUrl());
                String huaweiOssPrefix = NutzIocUtils.getOssPrefixHuaWei();
                String proxy = NutzIocUtils.getOssProxy();
                return sysOss.getUrl().replace(huaweiOssPrefix,"").replace(proxy,"");
            } else {
                return file.getAbsolutePath();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            if (returnWebUrl) FileUtil.del(file);
            IoUtil.close(outputStream);
            IoUtil.close(templateIs);
        }
    }

    /*******************************************************************************
     *用于获取模板路径的方法
     ******************************************************************************/
    public static String getTemplatePathByOS(String templateName) {
        if (StringUtils.isBlank(templateName)) Asserts.fail("模板文件不能为空");

        String osName = System.getProperty("os.name");
        String path = "";
        if (osName.startsWith("Windows")) {
            String filePathStr = "templet/formSummary/" + templateName;
            path = ClassLoader.getSystemResource(filePathStr).getPath();
        } else {
            Ioc ioc = NutzIocUtils.getIoc();
            PropertiesProxy propertiesProxy = ioc.getByType(PropertiesProxy.class);
            String projectPath = propertiesProxy.get("cusconfig.project-path");
            path = projectPath + "/file/formSummaryTemplate/" + templateName;
        }
        return path;
    }

    // 根据模板文件获取模板路径
    public static String getTemplatePathByName(String templateName) {
        if (StringUtils.isBlank(templateName)) Asserts.fail("模板文件不能为空");
        String osName = System.getProperty("os.name");
        String path = "";
        if (osName.startsWith("Windows")) {
            String filePathStr = "templet/formSummary/" + templateName;
            path = ClassLoader.getSystemResource(filePathStr).getPath();
        } else {
            Ioc ioc = NutzIocUtils.getIoc();
            PropertiesProxy propertiesProxy = ioc.getByType(PropertiesProxy.class);
            String projectPath = propertiesProxy.get("cusconfig.project-path");
            path = projectPath + "/file/sqfwTemplateV2/" + templateName;
        }
        return path;
    }

    public static File getTempExcelFile(String fileName) {
        if (StringUtils.isBlank(fileName)) fileName = IdUtil.fastSimpleUUID() + ExcelTypeEnum.XLSX.getValue();

        String osName = System.getProperty("os.name");

        String tempFilePath = "/tempFileDir/" + System.currentTimeMillis() + "/" + fileName;
        String path = "";
        if (osName.startsWith("Windows")) {
            String filePathStr = "templet/formSummary";
            path = ClassLoader.getSystemResource(filePathStr).getPath() + tempFilePath;
        } else {
            Ioc ioc = NutzIocUtils.getIoc();
            PropertiesProxy propertiesProxy = ioc.getByType(PropertiesProxy.class);
            String projectPath = propertiesProxy.get("cusconfig.project-path");
            path = projectPath + "/file/formSummaryTemplate/" + tempFilePath;
        }

        File file = new File(path);
        FileUtil.touch(file);
        return file;
    }

    public static byte[] getTempPdfFile(String fileName) {
        String osName = System.getProperty("os.name");

        String path = "";
        if (osName.startsWith("Windows")) {
            String filePathStr = "templet/formSummary/";
            path = ClassLoader.getSystemResource(filePathStr).getPath() + fileName;
        } else {
            Ioc ioc = NutzIocUtils.getIoc();
            PropertiesProxy propertiesProxy = ioc.getByType(PropertiesProxy.class);
            String projectPath = propertiesProxy.get("cusconfig.project-path");
            path = projectPath + "/file/formSummaryTemplate/" + fileName;
        }

        try {
            FileInputStream fileInputStream = new FileInputStream(path);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public static void easyExcelRepairPic(String excelPath) {
        File file = new File(excelPath);
        if (!file.isFile()) {
            throw new ServiceException("excel文件不存在");
        }
        if (!excelPath.endsWith(".xlsx")) {
            throw new ServiceException("文件格式必须为xlsx");
        }

        // 创建解压后的临时目录
        File tempDir = new File(file.getParent(), IdUtil.fastSimpleUUID());
        // 解压源文件到临时目录
        ZipUtil.unzip(file, tempDir);
        // 删除源文件
        FileUtil.del(file);
        // 压缩临时目录 替换源文件
        ZipUtil.zip(tempDir.getPath(), file.getPath());
        // 删除临时目录
        FileUtil.del(tempDir);
    }

    public static void addWordUserSignature(Map<String, Object> wordParam, String noSigParamName, String sigPicArrayName, String sigPicName, List<String> noSigUserNameList, List<String> sigUrlList, float sigHeight) {

        String ossPrefix = NutzIocUtils.getOssPrefix();

        List<String> picUrlList = Lists.newArrayList();
        if (noSigUserNameList == null) noSigUserNameList = Lists.newArrayList();
        if (sigUrlList == null) sigUrlList = Lists.newArrayList();
        for (String sigUrl : sigUrlList) {
            if (StringUtils.isNotBlank(sigUrl)) {
                if (sigUrl.startsWith("/"))
                    sigUrl = ossPrefix + sigUrl;
                picUrlList.add(sigUrl);
            }

            List<Map<String, PictureRenderData>> picList = addWordPicture(picUrlList, sigPicName, sigHeight);
            wordParam.put(sigPicArrayName, picList);
        }

        if (picUrlList.size() == 0) wordParam.put(sigPicArrayName, null);

        String noSigUserNameStr = Joiner.on("、").join(noSigUserNameList);
        if (sigUrlList.size() > 0 && StringUtils.isNotBlank(noSigUserNameStr)) noSigUserNameStr += "、";
        if (StringUtils.isBlank(noSigUserNameStr)) noSigUserNameStr = "";
        wordParam.put(noSigParamName, noSigUserNameStr);
    }

    /*******************************************************************************
     *使用OSS进行上传文件
     ******************************************************************************/
    public static SysOss uploadFile(ByteArrayOutputStream os, String fileName) {
        SysOssService sysOssService = (SysOssService) NutzIocUtils.getBeanByType(SysOssService.class);
        return sysOssService.upload(os.toByteArray(), fileName);
    }

    public static SysOss uploadFile(File file, String fileName) {
        SysOssService sysOssService = (SysOssService) NutzIocUtils.getBeanByType(SysOssService.class);
        return sysOssService.upload(file, fileName);
    }

    public static XWPFTemplate generateWordDocument(Map<String, Object> paramMap, Map<String, Object> footerMap,
                                                    String templateName) {
        return generateWordDocument(paramMap, footerMap, null, templateName);
    }

    public static XWPFTemplate generateWordDocument(Map<String, Object> paramMap, Map<String, Object> footerMap, Map<String, RenderPolicy> customerPolicyMap,
                                                    String templateName) {
        XWPFTemplate template = null;
        try {
            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();

            if (customerPolicyMap != null) {
                for (String key : customerPolicyMap.keySet()) {
                    RenderPolicy renderPolicy = customerPolicyMap.get(key);
                    builder.bind(key, renderPolicy);
                }
            }

            String templatePath = getTemplatePathByOS(templateName);

            template = XWPFTemplate.compile(templatePath, builder.build()).render(paramMap);

            // 设置页脚
            if (footerMap != null && footerMap.size() > 0) setXWPFFooter(template.getXWPFDocument(), footerMap);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return template;
    }

    /**
     * 根据模板创建word 输出流
     *
     * @param paramMap     参数
     * @param templateName 模板名称
     * @param formType     服务类型
     * @param startTime    开始时间
     * @return 输出流
     */
    @Deprecated
    public static ByteArrayOutputStream generateWordDocument(Map<String, Object> paramMap, String templateName, String formType, long startTime) {
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        try {
            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();

            String filePathStr = getTemplatePathByOS(templateName);
            XWPFTemplate template = XWPFTemplate.compile(filePathStr, builder.build()).render(paramMap);
            template.write(wordOutPutStream);

            System.out.println(StrFormatter.format("{}word生成成功，耗时{}", formType, (System.currentTimeMillis() - startTime)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wordOutPutStream;
    }


    /**
     * 设置页脚
     *
     * @param document  word文档
     * @param footerMap 页脚数据
     */
    public static void setXWPFFooter(XWPFDocument document, Map<String, Object> footerMap) {
        List<XWPFFooter> footerList = document.getFooterList();
        for (XWPFFooter footer : footerList) {
            List<XWPFParagraph> paragraphs = footer.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (int i = 0; i < runs.size(); i++) {
                    Set<String> keySet = footerMap.keySet();
                    String text = runs.get(i).getText(runs.get(i).getTextPosition());
                    Iterator<String> iterator = keySet.iterator();
                    while (iterator.hasNext()) {
                        String key = iterator.next();
                        if (text.contains(key)) {
                            text = text.replace("{" + key + "}", footerMap.get(key).toString());
                        }
                    }
                    runs.get(i).setText(text, 0);
                }
            }
        }
    }

    /**
     * wzy 从返回的url中得到文件名
     *
     * @param sysOss 上传结构
     * @return 结果
     */
    public static String getFilePathFromUploadFileUrl(SysOss sysOss) {
        if (sysOss == null) return null;
        if (StringUtils.isBlank(sysOss.getUrl())) return null;

        String fileName = sysOss.getFileName();
        if (StringUtils.isBlank(fileName)) return null;
        if (!fileName.startsWith("/")) fileName = "/" + fileName;
        return fileName;
    }

    /**
     * documents4j方式word转pdf
     *
     * @param file 源文件
     * @return 返回转成功的文件
     */
    public static File wordToPDFByDocuments4j(File file) {
        File outputFile = new File(String.format("%s%s%s%s", file.getParentFile(), File.separator, System.currentTimeMillis(), ".pdf"));
        try {
            InputStream docxInputStream = new FileInputStream(file);
            OutputStream outputStream = new FileOutputStream(outputFile);
            IConverter converter = LocalConverter.builder().build();
            converter.convert(docxInputStream).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute();
            outputStream.close();
        } catch (Exception e) {
            log.error("wordToPDFByDocuments4j error: ", e);
            throw new AppException("文件转换失败");
        }
        return outputFile;
    }

    public static GenerateWordAndPdfUrlVO wordAndUpload(Map<String, Object> paramMap, String templateName, String fileName, String formType) {
        long startTime = System.currentTimeMillis();

        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        try {

            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();

            String filePathStr = getTemplatePathByOS(templateName);
            XWPFTemplate template = XWPFTemplate.compile(filePathStr, builder.build()).render(paramMap);

            String wordUrl = "", pdfUrl = "";

            template.write(wordOutPutStream);

            // 上传word
            String wordFileName = "tempFireHidden.docx";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".docx";
            }
            SysOss sysOss = uploadFile(wordOutPutStream, wordFileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", formType, sysOss));
            wordUrl = sysOss.getUrl();
            GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
            resultVO.setWordUrl(wordUrl);
            resultVO.setPdfUrl(pdfUrl);
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
            return resultVO;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(wordOutPutStream);
        }
        return null;
    }

    /**
     * 生成word文档
     *
     * @param wordOutPutStream
     * @param fileName
     * @param formType
     * @return
     */
    public static GenerateWordAndPdfUrlVO wordUpload(ByteArrayOutputStream wordOutPutStream, String fileName, String formType) {
        long startTime = System.currentTimeMillis();
        try {
            String wordUrl = "", pdfUrl = "";
            // 上传word
            String wordFileName = "tempFireHidden.docx";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".docx";
            }
            SysOss sysOss = uploadFile(wordOutPutStream, wordFileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", formType, sysOss));
            wordUrl = sysOss.getUrl();
            GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
            resultVO.setWordUrl(wordUrl);
            resultVO.setPdfUrl(pdfUrl);
            resultVO.setUrl(getFilePathFromUploadFileUrl(sysOss));
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
            return resultVO;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(wordOutPutStream);
        }
        return null;
    }


    /**
     * word上传
     *
     * @param outputStreamList word的输出流数组
     * @param fileName         最后生成的文件名称
     * @param formType         安全
     * @return 结果
     */
    public static GenerateWordAndPdfUrlVO wordAndUpload(List<ByteArrayOutputStream> outputStreamList, String fileName, String formType) {
        long startTime = System.currentTimeMillis();

        ByteArrayOutputStream resultOS = new ByteArrayOutputStream();
        List<InputStream> pdfISList = Lists.newArrayList();
        List<ByteArrayOutputStream> pdfOSList = Lists.newArrayList();
        GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
        InputStream asposeLicInputStream = null;
        try {
            asposeLicInputStream = new ClassPathResource("/license.xml").getInputStream();
            License asposeLic = new License();
            asposeLic.setLicense(asposeLicInputStream);
            for (ByteArrayOutputStream outputStream : outputStreamList) {
                if (outputStream == null) continue;
                if (outputStream.size() == 0) continue;
                ByteArrayInputStream wordInputStream = new ByteArrayInputStream(outputStream.toByteArray());
                Document pdfDocument = new Document(wordInputStream);
                ByteArrayOutputStream pdfOS = new ByteArrayOutputStream();
                pdfDocument.save(pdfOS, SaveFormat.DOCX);
                pdfOSList.add(pdfOS);
            }

            PDFMergerUtility pdfMerger = new PDFMergerUtility();
            for (ByteArrayOutputStream pdfOS : pdfOSList) {
                byte[] bytes = pdfOS.toByteArray();
                InputStream pdfInputStream = new ByteArrayInputStream(bytes);
                pdfMerger.addSource(pdfInputStream);
                pdfISList.add(pdfInputStream);
            }
            pdfMerger.setDestinationStream(resultOS);
            pdfMerger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
            String pdfUrl = "", wordUrl = "";
            System.out.println("开始上传word");
            if (resultOS.size() == 0) throw new ServiceException("word转换出错");
            if (StringUtils.isBlank(fileName)) {
                fileName = "tempPdf.docx";
            } else {
                fileName += ".docx";
            }
            SysOss pdfUploadResponseBody = uploadFile(resultOS, fileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", formType, pdfUploadResponseBody));
            pdfUrl = getFilePathFromUploadFileUrl(pdfUploadResponseBody);
            resultVO.setWordUrl(wordUrl);
            resultVO.setPdfUrl(pdfUrl);
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            for (ByteArrayOutputStream outputStream : outputStreamList) {
                IoUtil.close(outputStream);
            }
            for (InputStream pdfIS : pdfISList) {
                IoUtil.close(pdfIS);
            }
            for (ByteArrayOutputStream pdfOS : pdfOSList) {
                IoUtil.close(pdfOS);
            }
            IoUtil.close(asposeLicInputStream);
            IoUtil.close(resultOS);
        }
        return resultVO;
    }

    /**
     * 生成word
     *
     * @param documentList
     * @param fileName
     * @return
     */
    public static GenerateWordAndPdfUrlVO wordUpload(List<NiceXWPFDocument> documentList, String fileName) {
        long startTime = System.currentTimeMillis();
        XWPFDocument doc = null;
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        try {
            for (int i = 0; i < documentList.size(); i++) {
                doc = documentList.get(0);
                if (i == 0 && documentList.size() > 1) {//首页直接分页，不再插入首页文档内容
                    documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE);
                } else if (i == 0 && documentList.size() == 1) {//只有一夜，就不分页
                    documentList.get(i).createParagraph().createRun();
                } else if (i == documentList.size() - 1) {//尾页不再分页，直接插入最后文档内容
                    appendBody(doc, documentList.get(i));
                } else {
                    documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE);
                    appendBody(doc, documentList.get(i));
                }
            }

            // 上传word
            String wordFileName = "模板.docx";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".docx";
            }

            doc.write(wordOutPutStream);
            SysOss sysOss = uploadFile(wordOutPutStream, wordFileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", "", sysOss));

            GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
            resultVO.setFileName(sysOss.getFileName());
            resultVO.setWordUrl(sysOss.getUrl());
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
            return resultVO;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(wordOutPutStream);
        }
        return null;
    }

    public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
        CTBody src1Body = src.getDocument().getBody();
        CTBody src2Body = append.getDocument().getBody();

        List<XWPFPictureData> allPictures = append.getAllPictures();
        // 记录图片合并前及合并后的ID
        Map<String, String> map = new HashMap<String, String>();
        for (XWPFPictureData picture : allPictures) {
            String before = append.getRelationId(picture);
            //将原文档中的图片加入到目标文档中
            String after = src.addPictureData(picture.getData(), org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_PNG);
            map.put(before, after);
        }
        appendBody(src1Body, src2Body, map);
    }

    private static void appendBody(CTBody src, CTBody append, Map<String, String> map) throws Exception {
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();
        String appendString = append.xmlText(optionsOuter);

        String srcString = src.xmlText();
        String prefix = srcString.substring(0, srcString.indexOf(">") + 1);
        String mainPart = srcString.substring(srcString.indexOf(">") + 1, srcString.lastIndexOf("<"));
        String sufix = srcString.substring(srcString.lastIndexOf("<"));
        String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
        if (map != null && !map.isEmpty()) {
            //对xml字符串中图片ID进行替换
            for (Map.Entry<String, String> set : map.entrySet()) {
                addPart = addPart.replace(set.getKey(), set.getValue());
            }
        }
        //将两个文档的xml内容进行拼接
        CTBody makeBody = CTBody.Factory.parse(prefix + mainPart + addPart + sufix);
        src.set(makeBody);
    }

    /**
     * word模板下载（该方法可以让每个word的页眉页脚保持原来的信息，不会在多个word合并后发生改变）
     *
     * @param documentList
     * @param fileName
     * @return
     */
    public static GenerateWordAndPdfUrlVO wordTemplateUpload(List<NiceXWPFDocument> documentList, String fileName) {
        long startTime = System.currentTimeMillis();
        com.spire.doc.Document doc = new com.spire.doc.Document();
        ByteArrayOutputStream wordOutPutStream = new ByteArrayOutputStream();
        try {
            for (NiceXWPFDocument document : documentList) {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                document.write(byteArrayOutputStream);
                InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                doc.insertTextFromStream(inputStream, FileFormat.Docm_2013);
            }

            // 上传word
            String wordFileName = "模板.docx";
            if (StringUtils.isNotBlank(fileName)) {
                wordFileName = fileName + ".docx";
            }

            // 把文件转成字节流
            doc.saveToStream(wordOutPutStream, FileFormat.Docm_2013);

            SysOss sysOss = uploadFile(wordOutPutStream, wordFileName);
            System.out.println(StrFormatter.format("{}word文件上传至华为云，返回结果:{}", "", sysOss));

            GenerateWordAndPdfUrlVO resultVO = new GenerateWordAndPdfUrlVO();
            resultVO.setWordUrl(sysOss.getUrl());
            resultVO.setTakeTime(System.currentTimeMillis() - startTime);
            return resultVO;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(wordOutPutStream);
            doc.close();
        }
        return null;
    }

    /**
     * 从word获取不带格式的文本
     *
     * @param templateName
     * @return
     */
    public static String wordToString(String templateName) {
        // 获取完整路径
        String templatePath = getTemplatePathByOS(templateName);
        File file = new File(templatePath);
        if (!file.exists()) {
            throw new RuntimeException("文件" + templateName + "不存在");
        }
        if (file.getName().toLowerCase().endsWith(".docx")) {
            try (InputStream in = new FileInputStream(file)) {
                StringBuilder result = new StringBuilder();
                XWPFDocument document = new XWPFDocument(in);
                XWPFWordExtractor re = new XWPFWordExtractor(document);
                result.append(re.getText());
                re.close();
                return result.toString();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        } else {
            throw new RuntimeException("请把文件后缀改为docx");
        }
    }

    /**
     * excel内容转成文本
     *
     * @param templateName
     * @return
     */
    public static String excelToString(String templateName) {
        // 获取完整路径
        String templatePath = getTemplatePathByOS(templateName);
        FileInputStream inputStream = null;
        XSSFWorkbook workbook = null;
        try {
            inputStream = new FileInputStream(templatePath);
            workbook = new XSSFWorkbook(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        StringBuilder stringBuilder = new StringBuilder();
        Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
        int rowCount = sheet.getLastRowNum() - sheet.getFirstRowNum() + 1;
        for (int i = 0; i < rowCount; i++) {
            Row row = sheet.getRow(i);
            int columnCount = row.getLastCellNum() - row.getFirstCellNum();
            for (int j = 0; j < columnCount; j++) {
                Cell cell = row.getCell(j);
                stringBuilder.append(cell.toString());
            }
        }
        try {
            workbook.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }

    /**
     * 获取word模板需要填值的标签名称（如：{{xxx}}、{{?xxx}}）
     *
     * @param templateName
     * @return
     */
    public static HashMap<String, Object> getWordTag(String templateName) {
        HashMap<String, Object> map = new HashMap<>();

        // 获取{{xxx}}里的内容 （文本标签）
        Pattern p1 = Pattern.compile("\\{\\{([^\\}]+)");
        // 获取{{?xxx}}里的内容 (图片标签)
        Pattern p2 = Pattern.compile("\\{\\{\\?([^\\}]+)");
        // {{problem==0?'☑':'□'}}  (表达式)
        Pattern p3 = Pattern.compile("\\{\\{([^!\\}.*\\=\\=]+)");

        // 获取word模板内容
        String text = wordToString(templateName);

        // 获取文本标签
        Matcher matcher1 = p1.matcher(text);
        while (matcher1.find()) {
            map.put(matcher1.group(1), null);
        }

        // 获取图片标签
        Matcher matcher2 = p2.matcher(text);
        while (matcher2.find()) {
            map.put(matcher2.group(1), new ArrayList<>());
        }

        // 表达式
        Matcher matcher3 = p3.matcher(text);
        while (matcher3.find()) {
            map.put(matcher3.group(1), null);
        }

        return map;
    }

    /**
     * 获取Excel模板需要填值的标签名称（如：{xxx}、{data.xxx}）
     *
     * @param templateName
     * @return
     */
    public static HashMap<String, Object> getExcelTag(String templateName) {
        HashMap<String, Object> map = new HashMap<>();

        // 获取{xxx}里的内容 （文本标签）
        Pattern p1 = Pattern.compile("\\{([^!\\..*\\}]+)");
        // 获取{data.xxx}里的内容 (图片标签)
        Pattern p2 = Pattern.compile("\\{data\\.([^\\}]+)");

        // 获取excel模板内容
        String text = excelToString(templateName);

        // 获取文本标签
        Matcher matcher1 = p1.matcher(text);
        while (matcher1.find()) {
            map.put(matcher1.group(1), null);
        }

        // 获取文本标签
        Matcher matcher2 = p2.matcher(text);
        while (matcher2.find()) {
            map.put(matcher2.group(1), null);
        }
        return map;
    }
}
